﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Windows.Forms;
using Autodesk.Revit.ApplicationServices;
using Autodesk.Revit.DB;
using Autodesk.Revit.DB.Events;
using Autodesk.Revit.UI;
using YArchitech.Controls;
using YArchitech.Revit;
using YJKArchMethodLibrary.Utils;
using YJKCodeBase.WinForm;
using YJKElementFinish.ColumnSurface.DataDefinition;
using YJKElementFinish.ColumnSurface.Managers;
using YJKElementFinish.WallInsulationLayer.Forms;
using TopologySearch;

namespace YJKElementFinish.WallInsulationLayer.Controllers
{
	public class AddInsulationLayerController
	{
		public AddInsulationLayerController(ExternalCommandData cmdData)
		{
			this.m_cmdData = cmdData;
			this.m_app = this.m_cmdData.Application.Application;
			this.m_doc = cmdData.Application.ActiveUIDocument.Document;
			this.m_lstAddedElementIds = new List<ElementId>();
		}

		public Result CreateInsulationLayer()
		{
			if (AddSurfaceLayersController._hWndRevit == null)
			{
				Process currentProcess = Process.GetCurrentProcess();
				IntPtr mainWindowHandle = currentProcess.MainWindowHandle;
				AddSurfaceLayersController._hWndRevit = new WindowHandle(mainWindowHandle);
			}
			List<Level> levelsExcludeBelowCurrent = this.GetLevelsExcludeBelowCurrent();
			if (this.m_bUseDefaultHeight)
			{
				YJKMessageBox.Show("无法检测到当前视图标高之上存在任何标高，系统将默认按照3000mm墙高创建。", MessageBoxButtons.OK);
			}
			List<WallType> basicWallTypes = this.GetBasicWallTypes();
			if (basicWallTypes.Count == 0)
			{
				return Autodesk.Revit.UI.Result.Cancelled;
			}
			AddInsulationLayerForm addInsulationLayerForm = new AddInsulationLayerForm(this.m_doc, levelsExcludeBelowCurrent, basicWallTypes);
			List<PositioningCurveController.CurveSegmentInfo> list = new List<PositioningCurveController.CurveSegmentInfo>();
			TransactionGroup transactionGroup = new TransactionGroup(this.m_doc, "Create Insulation Layer");
			Result result;
			try
			{
				transactionGroup.Start();
				while (DialogResult.OK == addInsulationLayerForm.ShowDialog())
				{
					if (AddInsulationLayerForm.FormResultStyle.FRS_Draw == addInsulationLayerForm.ResultStyle)
					{
						if (list.Count == 0 && !this.AutoSelectOutWallCurve(ref list))
						{
							if (1 == (int)transactionGroup.GetStatus())
							{
								transactionGroup.RollBack();
							}
							return Autodesk.Revit.UI.Result.Cancelled;
						}
						this.EditOutWallCurve(ref list);
						if (PositioningCurveController.CreateWall)
						{
							addInsulationLayerForm.Close();
							List<WallTypeLayerInfos> list2 = this.SelectWallTypeLayerInfo(addInsulationLayerForm);
							if (list2.Count == 0)
							{
								if (1 == (int)transactionGroup.GetStatus())
								{
									transactionGroup.RollBack();
								}
								return Autodesk.Revit.UI.Result.Cancelled;
							}
							this.m_lstAddedElementIds.Clear();
							this.m_app.DocumentChanged += this.App_DocumentChanged;
							List<Wall> wallsOnLevel = this.GetWallsOnLevel();
							this.CreateInsulationLayerWall(list2);
							this.JoinWalls(list, wallsOnLevel, addInsulationLayerForm.SelectLevelValue);
							this.ModifyInsulationLayerHeight(addInsulationLayerForm.SelectLevelValue);
							this.m_app.DocumentChanged -= this.App_DocumentChanged;
							this.DeletePositioningCurves(list);
							transactionGroup.Assimilate();
							return Autodesk.Revit.UI.Result.Succeeded;
						}
					}
				}
				this.DeletePositioningCurves(list);
				if (1 == (int)transactionGroup.GetStatus())
				{
					transactionGroup.RollBack();
				}
				result = Autodesk.Revit.UI.Result.Cancelled;
			}
			catch (Exception)
			{
				this.DeletePositioningCurves(list);
				if (1 == (int)transactionGroup.GetStatus())
				{
					transactionGroup.RollBack();
				}
				result = Autodesk.Revit.UI.Result.Failed;
			}
			return result;
		}

		private bool AutoSelectOutWallCurve(ref List<PositioningCurveController.CurveSegmentInfo> lstRefCurveInfos)
		{
			AddSurfaceLayersController addSurfaceLayersController = new AddSurfaceLayersController(this.m_cmdData);
			List<Curve> lines = new List<Curve>();
			if (!addSurfaceLayersController.SelectExternalProfileByLinkDoc(ref lines, true))
			{
				return false;
			}
			PositioningCurveController positioningCurveController = new PositioningCurveController(this.m_cmdData, lstRefCurveInfos);
			positioningCurveController.DrawLines(lines, ref lstRefCurveInfos);
			return true;
		}

		private void EditOutWallCurve(ref List<PositioningCurveController.CurveSegmentInfo> lstCurveSegmentInfos)
		{
			PositioningCurveController positioningCurveController = new PositioningCurveController(this.m_cmdData, lstCurveSegmentInfos);
			string text = "";
			positioningCurveController.EditRefCurve(ref lstCurveSegmentInfos, ref text);
		}

		private void CreateInsulationLayerWall(List<WallTypeLayerInfos> lstWallTypeLayerInfos)
		{
			AddSurfaceLayersController addSurfaceLayersController = new AddSurfaceLayersController(this.m_cmdData);
			addSurfaceLayersController.AddSurfaceByRefCurve(lstWallTypeLayerInfos);
		}

		private List<Level> GetLevelsExcludeBelowCurrent()
		{
			List<Level> list = new List<Level>();
			Level genLevel = this.m_doc.ActiveView.GenLevel;
			List<Level> sortLevels = Common.GetSortLevels(this.m_doc, double.MinValue, double.MaxValue);
			if (1 == sortLevels.Count)
			{
				this.m_bUseDefaultHeight = true;
				return sortLevels;
			}
			int i = 0;
			while (i < sortLevels.Count)
			{
				Level level = sortLevels.ElementAt(i);
				if (!(genLevel.Id != level.Id))
				{
					if (i == sortLevels.Count - 1)
					{
						this.m_bUseDefaultHeight = true;
						list.Add(sortLevels[i]);
						break;
					}
					list = sortLevels.GetRange(i + 1, sortLevels.Count - i - 1);
					break;
				}
				else
				{
					i++;
				}
			}
			return list;
		}

		private List<WallType> GetBasicWallTypes()
		{
			List<WallType> list = new List<WallType>();
			FilteredElementCollector filteredElementCollector = new FilteredElementCollector(this.m_doc);
			ICollection<Element> collection = filteredElementCollector.OfClass(typeof(WallType)).ToElements();
			foreach (Element element in collection)
			{
				WallType wallType = element as WallType;
				if (wallType != null && wallType.Kind == null)
				{
					list.Add(element as WallType);
				}
			}
			return list;
		}

		private List<WallTypeLayerInfos> SelectWallTypeLayerInfo(AddInsulationLayerForm dlg)
		{
			List<WallTypeLayerInfos> list = new List<WallTypeLayerInfos>();
			if (dlg.CreateNewWallType)
			{
				WallType wallType = null;
				List<WallType> basicWallTypes = this.GetBasicWallTypes();
				foreach (WallType wallType2 in basicWallTypes)
				{
					if (wallType2.Name == dlg.WallName)
					{
						wallType = wallType2;
						break;
					}
				}
				if (wallType == null)
				{
					wallType = this.CreateInsulationWallType(dlg.WallName, Common.MMToFeet(dlg.Thick));
					WallTypeLayerInfos wallLayerInfoListByType = WallTypeManager.GetWallLayerInfoListByType(wallType, this.m_doc, Common.MMToFeet(dlg.Thick));
					list.Add(wallLayerInfoListByType);
				}
				else
				{
					WallTypeLayerInfos wallLayerInfoListByType2 = WallTypeManager.GetWallLayerInfoListByType(wallType, this.m_doc);
					list.Add(wallLayerInfoListByType2);
				}
			}
			else
			{
				WallTypeLayerInfos wallLayerInfoListByType3 = WallTypeManager.GetWallLayerInfoListByType(dlg.SelectWallType, this.m_doc);
				list.Add(wallLayerInfoListByType3);
			}
			return list;
		}

		private WallType CreateInsulationWallType(string strInsulationLayerName, double dThick)
		{
			List<WallType> basicWallTypes = this.GetBasicWallTypes();
			Transaction transaction = new Transaction(this.m_doc, "Create New WallType");
			transaction.Start();
			WallType wallType = basicWallTypes.ElementAt(0).Duplicate(strInsulationLayerName) as WallType;
			List<CompoundStructureLayer> list = new List<CompoundStructureLayer>();
            CompoundStructureLayer item = new CompoundStructureLayer(dThick, (MaterialFunctionAssignment)3, this.GetMaterialId());
			list.Add(item);
			CompoundStructure compoundStructure = CompoundStructure.CreateSimpleCompoundStructure(list);
			wallType.SetCompoundStructure(compoundStructure);
			transaction.Commit();
			return wallType;
		}

		private ElementId GetMaterialId()
		{
			List<Material> allMaterial = Common.GetAllMaterial(this.m_doc);
			ElementId elementId = null;
			foreach (Material material in allMaterial)
			{
				if ("聚苯板" == material.Name)
				{
					elementId = material.Id;
					break;
				}
			}
			if (null == elementId)
			{
				Material material2 = allMaterial[0].Duplicate("聚苯板");
				Color cutPatternColor = new Color(byte.MaxValue, 0, byte.MaxValue);
				material2.CutPatternColor = cutPatternColor;
				Common.SetMaterialByFillPattern(this.m_doc, "松散-泡沫塑料", ref material2, true);
				elementId = material2.Id;
			}
			return elementId;
		}

		private void ModifyInsulationLayerHeight(string strLevelValue)
		{
			Transaction transaction = new Transaction(this.m_doc, "modify wall height");
			try
			{
				transaction.Start();
				Level level = null;
				List<Level> levelsExcludeBelowCurrent = this.GetLevelsExcludeBelowCurrent();
				foreach (Level level2 in levelsExcludeBelowCurrent)
				{
					if (level2.Name == strLevelValue)
					{
						level = level2;
						break;
					}
				}
				if (level == null && this.m_bUseDefaultHeight)
				{
					this.m_bUseDefaultHeight = false;
				}
				foreach (ElementId elementId in this.m_lstAddedElementIds)
				{
					Element element = this.m_doc.GetElement(elementId);
					if (element is Wall)
					{
						Wall elem = element as Wall;
						if (this.m_bUseDefaultHeight)
						{
							elem.GetParameter(BuiltInParameter.WALL_USER_HEIGHT_PARAM).Set(Common.MMToFeet(3000.0));
						}
						else if (level != null)
						{
							elem.GetParameter(BuiltInParameter.WALL_HEIGHT_TYPE).Set(level.Id);
						}
						else
						{
							elem.GetParameter(BuiltInParameter.WALL_USER_HEIGHT_PARAM).Set(Common.MMToFeet(Convert.ToDouble(strLevelValue)));
						}
					}
				}
				transaction.Commit();
			}
			catch (Exception)
			{
				transaction.RollBack();
			}
		}

		private void GetDrawCurveIds(List<PositioningCurveController.CurveSegmentInfo> lstRefCurveInfos, ref List<ElementId> lstRetIds)
		{
			if (lstRefCurveInfos.Count == 0)
			{
				return;
			}
			foreach (PositioningCurveController.CurveSegmentInfo curveSegmentInfo in lstRefCurveInfos)
			{
				lstRetIds.Add(curveSegmentInfo.CurveId);
				lstRetIds.AddRange(curveSegmentInfo.ArrowLineIds);
			}
		}

		private void DeletePositioningCurves(List<PositioningCurveController.CurveSegmentInfo> lstRefCurveInfos)
		{
			List<ElementId> list = new List<ElementId>();
			this.GetDrawCurveIds(lstRefCurveInfos, ref list);
			if (list.Count == 0)
			{
				return;
			}
			PositioningCurveController.DeleteElements(this.m_doc, list);
		}

		private void App_DocumentChanged(object sender, DocumentChangedEventArgs e)
		{
			IList<ElementId> list = e.GetAddedElementIds() as List<ElementId>;
			foreach (ElementId elementId in list)
			{
				if (this.m_doc.GetElement(elementId) is Wall)
				{
					this.m_lstAddedElementIds.Add(elementId);
				}
			}
		}

		private void JoinWalls(List<PositioningCurveController.CurveSegmentInfo> lstRefCurveInfos, List<Wall> lstAllWalls, string strSelectLevelValue)
		{
			if (lstRefCurveInfos.Count == 0 || lstAllWalls.Count == 0 || "" == strSelectLevelValue)
			{
				return;
			}
			List<Element> conformWalls = this.GetConformWalls(strSelectLevelValue, lstAllWalls);
			if (conformWalls == null)
			{
				return;
			}
			Transaction transaction = new Transaction(this.m_doc);
			try
			{
				transaction.Start("join walls with insulation layer");
				foreach (PositioningCurveController.CurveSegmentInfo curveSegmentInfo in lstRefCurveInfos)
				{
					DetailCurve detailCurve = this.m_doc.GetElement(curveSegmentInfo.CurveId) as DetailCurve;
					if (detailCurve != null)
					{
						Curve geometryCurve = detailCurve.GeometryCurve;
						if (!(null == geometryCurve))
						{
							List<Wall> wallByRefCurve = this.GetWallByRefCurve(geometryCurve);
							List<Element> joinWallsByCurve = this.GetJoinWallsByCurve(conformWalls, geometryCurve);
							if (joinWallsByCurve != null)
							{
								foreach (Wall wall in wallByRefCurve)
								{
									foreach (Element element in joinWallsByCurve)
									{
										if (!JoinGeometryUtils.AreElementsJoined(this.m_doc, wall, element))
										{
											JoinGeometryUtils.JoinGeometry(this.m_doc, wall, element);
										}
									}
								}
							}
						}
					}
				}
				transaction.Commit();
			}
			catch (Exception)
			{
				transaction.RollBack();
			}
		}

		private List<Element> GetConformWalls(string strSelectLevelValue, List<Wall> lstAllWalls)
		{
			if (lstAllWalls.Count == 0 || "" == strSelectLevelValue)
			{
				return null;
			}
			List<Element> list = new List<Element>();
			double elevation = this.m_doc.ActiveView.GenLevel.Elevation;
			List<Level> levelsExcludeBelowCurrent = this.GetLevelsExcludeBelowCurrent();
			Level level = null;
			foreach (Level level2 in levelsExcludeBelowCurrent)
			{
				if (level2.Name == strSelectLevelValue)
				{
					level = level2;
					break;
				}
			}
			double num;
			if (level == null)
			{
				num = elevation + Common.MMToFeet(Convert.ToDouble(strSelectLevelValue));
			}
			else if (this.m_bUseDefaultHeight)
			{
				num = elevation + Common.MMToFeet(3000.0);
			}
			else
			{
				num = level.Elevation;
			}
			foreach (Wall wall in lstAllWalls)
			{
				Curve curve = (wall.Location as LocationCurve).Curve;
				double z = curve.GetEndPoint(0).Z;
				if (1E-06 <= z - elevation && 1E-06 < num - z)
				{
					list.Add(wall);
				}
			}
			return list;
		}

		private bool IsExistingElement(Element elemCheck, List<Element> lstElements)
		{
			bool result = false;
			foreach (Element element in lstElements)
			{
				if (elemCheck.Id == element.Id)
				{
					result = true;
					break;
				}
			}
			return result;
		}

		private List<Wall> GetWallsOnLevel()
		{
			List<Wall> list = new List<Wall>();
			double elevation = this.m_doc.ActiveView.GenLevel.Elevation;
			FilteredElementCollector filteredElementCollector = new FilteredElementCollector(this.m_doc);
			ICollection<Element> collection = filteredElementCollector.OfClass(typeof(Wall)).ToElements();
			foreach (Element element in collection)
			{
				Wall wall = element as Wall;
				double z = (wall.Location as LocationCurve).Curve.GetEndPoint(0).Z;
				if (1E-06 <= z - elevation)
				{
					list.Add(wall);
				}
			}
			return list;
		}

		private List<Wall> GetWallByRefCurve(Curve refCurve)
		{
			List<Wall> result;
			try
			{
				List<WallGeometryInfo> list = new List<WallGeometryInfo>();
				foreach (ElementId elementId in this.m_lstAddedElementIds)
				{
					Wall wall = this.m_doc.GetElement(elementId) as Wall;
					if (wall != null)
					{
						List<Solid> elementSolids = ElementCommon.GetElementSolids(this.m_cmdData.Application, wall);
						CurveArray projection = new CurveArray();
						ElementCommon.GetSolidProjection(this.m_doc, elementSolids, ref projection);
						WallGeometryInfo item = new WallGeometryInfo(wall, projection);
						list.Add(item);
					}
				}
				List<Wall> wallByRefCurve = this.GetWallByRefCurve(refCurve, list);
				result = wallByRefCurve;
			}
			catch (Exception)
			{
				result = null;
			}
			return result;
		}

		private List<Wall> GetWallByRefCurve(Curve detailCurve, List<WallGeometryInfo> CurrentLayerWallInfos)
		{
			List<Wall> list = new List<Wall>();
			foreach (WallGeometryInfo wallGeometryInfo in CurrentLayerWallInfos)
			{
				if (wallGeometryInfo.IsWallFace(detailCurve))
				{
					list.Add(wallGeometryInfo.Component);
				}
			}
			return list;
		}

		private List<Element> GetJoinWallsByCurve(List<Element> lstConformWalls, Curve curve)
		{
			if (lstConformWalls.Count == 0 || null == curve)
			{
				return null;
			}
			List<Element> list = new List<Element>();
			XYZ endPoint = curve.GetEndPoint(0);
			XYZ endPoint2 = curve.GetEndPoint(1);
			Line line = Line.CreateUnbound(endPoint, XYZ.BasisZ);
			Line line2 = Line.CreateUnbound(endPoint2, XYZ.BasisZ);
			foreach (Element element in lstConformWalls)
			{
				Wall wall = element as Wall;
				Curve curve2 = (wall.Location as LocationCurve).Curve;
				if (!(null == curve2))
				{
					Pole2D pole2D;
					if (curve2.IsCyclic)
					{
						pole2D = Pole2D.NewStructure2DPole(wall.Id, curve2.GetEndPoint(0), curve2.GetEndPoint(1), (curve2 as Arc).Center, (curve2 as Arc).Normal, wall.Width / 2.0, wall.Width / 2.0);
					}
					else
					{
						pole2D = Pole2D.NewStructure2DPole(wall.Id, curve2.GetEndPoint(0), curve2.GetEndPoint(1), wall.Width / 2.0, wall.Width / 2.0);
					}
					IntersectionResultArray intersectionResultArray = new IntersectionResultArray();
					line.Intersect(pole2D.PositiveCurve(), out intersectionResultArray);
					if (intersectionResultArray != null && intersectionResultArray.Size > 0 && !this.IsExistingElement(element, list))
					{
						list.Add(element);
					}
                    line2.Intersect(pole2D.NegativeCurve(), out intersectionResultArray);
					if (intersectionResultArray != null && intersectionResultArray.Size > 0 && !this.IsExistingElement(element, list))
					{
						list.Add(element);
					}
				}
			}
			return list;
		}

		private ExternalCommandData m_cmdData;

        private Autodesk.Revit.ApplicationServices.Application m_app;

		private Document m_doc;

		private List<ElementId> m_lstAddedElementIds;

		private bool m_bUseDefaultHeight;
	}
}
